/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include <QQmlEngine>
#include <QQmlContext>
#include <KWindowSystem>
#include <KWindowEffects>
#include <QScreen>
#include <QApplication>
#include <QPainterPath>
#include <kysdk/applications/windowmanager/windowmanager.h>

#include "popup-notification-window.h"
#include "notification-group-model.h"
#include "notification-model.h"
#include "screen-monitor.h"
#include "popup-notification-model.h"

using namespace UkuiNotification;
PopupNotificationWindow::PopupNotificationWindow(QWindow *parent) : SharedEngineView(parent)
{
    initWindow();
    initNotificationModel();
}

void PopupNotificationWindow::initWindow()
{
    setResizeMode(SharedEngineView::SizeViewToRootObject);
    setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    setColor("transparent");
    KWindowSystem::setType(this->winId(), NET::Notification);

    // 监听窗口尺寸变化,屏幕变化
    setScreen(Sidebar::ScreenMonitor::getInstance()->getPrimaryScreen());
    updateGeometry();

    connect(Sidebar::ScreenMonitor::getInstance(), &Sidebar::ScreenMonitor::primaryScreenChanged, this, [this] {
        setScreen(Sidebar::ScreenMonitor::getInstance()->getPrimaryScreen());
        updateGeometry();
    });
    connect(Sidebar::ScreenMonitor::getInstance(), &Sidebar::ScreenMonitor::geometryChanged, this, &PopupNotificationWindow::updateGeometry);
    connect(Sidebar::ScreenMonitor::getInstance(), &Sidebar::ScreenMonitor::panelPropertyChanged, this, &PopupNotificationWindow::updateGeometry);
}

void PopupNotificationWindow::initNotificationModel()
{
    auto popupFilterModel = new PopupNotificationModel(this);
    popupFilterModel->setSourceModel(NotificationModel::instance());

    auto groupModel = new NotificationGroupModel(this);
    groupModel->setSourceModel(popupFilterModel);

    rootContext()->setContextProperty("groupModel", groupModel);
    rootContext()->setContextProperty("sourceModel", NotificationModel::instance());
    rootContext()->setContextProperty("popupNotificationWindow", this);

    connect(groupModel, &QAbstractItemModel::rowsInserted, groupModel, [this, groupModel] {
        setVisible(groupModel->rowCount(QModelIndex()) > 0);
    });
    connect(groupModel, &QAbstractItemModel::rowsRemoved, groupModel, [this, groupModel] {
        setVisible(groupModel->rowCount(QModelIndex()) > 0);
    });
}

void PopupNotificationWindow::loadQML()
{
    engine()->addImportPath("qrc:/qml");
    setSource(QUrl("qrc:/qml/PopupView.qml"));
}

void PopupNotificationWindow::updateGeometry()
{
    Sidebar::ScreenMonitor *screenMonitor = Sidebar::ScreenMonitor::getInstance();
    QRect screenRect = screenMonitor->getGeometry();

    int margin = 8;
    int panelSize = screenMonitor->getPanelSize();
    int panelPosition = screenMonitor->getPanelPosition();
    int maxWindowHeight;

    switch(panelPosition) {
        default:
        case 0: {
            m_notificationPoint = QPoint(screenRect.width() - m_viewWidth - margin, margin);
            maxWindowHeight = screenRect.height() - panelSize - margin * 2;
            break;
        }
        case 1: {
            m_notificationPoint = QPoint(screenRect.width() - m_viewWidth - margin, margin + panelSize);
            maxWindowHeight = screenRect.height() - panelSize - margin * 2;
            break;
        }
        case 2: {
            m_notificationPoint = QPoint(screenRect.width() - m_viewWidth - margin, margin);
            maxWindowHeight = screenRect.height() - margin * 2;
            break;
        }
        case 3: {
            m_notificationPoint = QPoint(screenRect.width() - m_viewWidth - margin - panelSize, margin);
            maxWindowHeight = screenRect.height() - margin * 2;
            break;
        }
    }

    setMaximumHeight(maxWindowHeight);
    // 多屏状态下，坐标位置确定
    m_notificationPoint.setX(m_notificationPoint.x() + screenRect.x());
    m_notificationPoint.setY(m_notificationPoint.y() + screenRect.y());

    if (m_isSidebarShow) {
        m_notificationPoint.setX(m_notificationPoint.x() - m_sidebarWidth);
    }

    kdk::WindowManager::setGeometry(this, QRect(m_notificationPoint, QSize(width(), height())));
}

void PopupNotificationWindow::updataWindowRegion(QVariantMap windowRect, int contentY)
{
    QVariantList groupsRect = windowRect.value("regions").toList();
    QRegion windowRegion;
    int nextGroupsY = 0;

    for (int i = 0; i < groupsRect.length(); i ++) {
        int count = groupsRect.at(i).toMap().value("count").toInt();
        int height = groupsRect.at(i).toMap().value("height").toInt();
        int width = groupsRect.at(i).toMap().value("width").toInt();
        int radius = groupsRect.at(i).toMap().value("radius").toInt();
        int spacing = 8;
        int groupsRightBottom = 0;
        QRegion childRegion;
        QPainterPath path;

        path.addRoundedRect(0, 0, width, height, radius, radius);
        QRegion groupsRegion = QRegion(path.toFillPolygon().toPolygon());

        if (count == 1) {
            childRegion = groupsRegion;
            groupsRightBottom = height;

        } else if (count == 2) {
            path.addRoundedRect((0.05 * width) / 2, spacing, width * 0.95, height, radius, radius);
            QRegion folderRegion = QRegion(path.toFillPolygon().toPolygon());
            childRegion = groupsRegion.united(folderRegion);
            groupsRightBottom = height + spacing;

        } else if (count > 2) {
            path.addRoundedRect((0.05 * width) / 2, spacing, width * 0.95, height, radius, radius);
            QRegion folderRegion = QRegion(path.toFillPolygon().toPolygon());
            path.addRoundedRect((0.0975 * width) / 2, spacing * 2, width * 0.9025, height, radius, radius);
            QRegion secondFolderRegion = QRegion(path.toFillPolygon().toPolygon());
            childRegion = groupsRegion.united(folderRegion).united(secondFolderRegion);
            groupsRightBottom = height + spacing * 2;
        }

        if (i > 0) {
            childRegion.translate(0, nextGroupsY);
        }

        nextGroupsY = nextGroupsY + groupsRightBottom + spacing;
        windowRegion = windowRegion.united(childRegion);
    }
    m_windowRegion = windowRegion.translated(0, -contentY);
    enableWindowBlur(m_enable);
}

void PopupNotificationWindow::updataGroupsPosition(int relativeY)
{
    m_windowRegion.translate(QPoint(0, -relativeY));
}

void PopupNotificationWindow::enableWindowBlur(bool enable)
{
    m_enable = enable;
    KWindowEffects::enableBlurBehindWithStrength(this, m_enable, m_windowRegion, 800);
}

void PopupNotificationWindow::updateWindowPosition(bool isSidebarShow, int sidebarWidth)
{
    m_isSidebarShow = isSidebarShow;
    m_sidebarWidth = sidebarWidth;
    int forward = isSidebarShow ? 1 : -1;
    m_notificationPoint.setX(m_notificationPoint.x() - sidebarWidth * forward);
    kdk::WindowManager::setGeometry(this, QRect(m_notificationPoint, QSize(width(), height())));
}

bool PopupNotificationWindow::event(QEvent *event)
{
    switch (event->type()) {
        case QEvent::Expose: {
            if (isExposed()) {
                KWindowSystem::setType(this->winId(), NET::Notification);
                kdk::WindowManager::setSkipTaskBar(this, true);
                kdk::WindowManager::setSkipSwitcher(this, true);
                kdk::WindowManager::setGeometry(this, QRect(m_notificationPoint, QSize(width(), height())));
            }
            break;
        }
        default:
            break;
    }
    return QQuickWindow::event(event);
}

int PopupNotificationWindow::viewWidth() const
{
    return m_viewWidth;
}

int PopupNotificationWindow::itemWidth() const
{
    return m_itemWidth;
}
